/**
 * Software License, Version 1.0
 * 
 * Copyright 2003 The Trustees of Indiana University.  All rights reserved.
 * 
 *
 *Redistribution and use in source and binary forms, with or without 
 *modification, are permitted provided that the following conditions are met:
 *
 *1) All redistributions of source code must retain the above copyright notice,
 * the list of authors in the original source code, this list of conditions and
 * the disclaimer listed in this license;
 *2) All redistributions in binary form must reproduce the above copyright 
 * notice, this list of conditions and the disclaimer listed in this license in
 * the documentation and/or other materials provided with the distribution;
 *3) Any documentation included with all redistributions must include the 
 * following acknowledgement:
 *
 *"This product includes software developed by the Community Grids Lab. For 
 * further information contact the Community Grids Lab at 
 * http://communitygrids.iu.edu/."
 *
 * Alternatively, this acknowledgement may appear in the software itself, and 
 * wherever such third-party acknowledgments normally appear.
 * 
 *4) The name Indiana University or Community Grids Lab or NaradaBrokering, 
 * shall not be used to endorse or promote products derived from this software 
 * without prior written permission from Indiana University.  For written 
 * permission, please contact the Advanced Research and Technology Institute 
 * ("ARTI") at 351 West 10th Street, Indianapolis, Indiana 46202.
 *5) Products derived from this software may not be called NaradaBrokering, 
 * nor may Indiana University or Community Grids Lab or NaradaBrokering appear
 * in their name, without prior written permission of ARTI.
 * 
 *
 * Indiana University provides no reassurances that the source code provided 
 * does not infringe the patent or any other intellectual property rights of 
 * any other entity.  Indiana University disclaims any liability to any 
 * recipient for claims brought by any other entity based on infringement of 
 * intellectual property rights or otherwise.  
 *
 *LICENSEE UNDERSTANDS THAT SOFTWARE IS PROVIDED "AS IS" FOR WHICH NO 
 *WARRANTIES AS TO CAPABILITIES OR ACCURACY ARE MADE. INDIANA UNIVERSITY GIVES
 *NO WARRANTIES AND MAKES NO REPRESENTATION THAT SOFTWARE IS FREE OF 
 *INFRINGEMENT OF THIRD PARTY PATENT, COPYRIGHT, OR OTHER PROPRIETARY RIGHTS. 
 *INDIANA UNIVERSITY MAKES NO WARRANTIES THAT SOFTWARE IS FREE FROM "BUGS", 
 *"VIRUSES", "TROJAN HORSES", "TRAP DOORS", "WORMS", OR OTHER HARMFUL CODE.  
 *LICENSEE ASSUMES THE ENTIRE RISK AS TO THE PERFORMANCE OF SOFTWARE AND/OR 
 *ASSOCIATED MATERIALS, AND TO THE PERFORMANCE AND VALIDITY OF INFORMATION 
 *GENERATED USING SOFTWARE.
 */
package cgl.narada.performance;

import java.util.Collections;
import java.util.Enumeration;
import java.util.Hashtable;
import java.util.Vector;
/**
 *
 * @author John Yin
 */

public class StatCollection {

    public static final byte AVG_STAT     = 0;
    public static final byte MAX_STAT     = 1;
    public static final byte MIN_STAT     = 2;
    // Defined as average inter-arrival distance from samples in 25% - 75% range
    public static final byte JITTER_STAT  = 3;

    /**
     * Average value over time of collection. X / second.
     */
    public static final byte TIME_AVG_STAT = 4;

    private int       m_maxSamples = 100;
    private Hashtable m_metrics    = new Hashtable();


    /**
     * -----------------------------------------------------------------
     * Maximum number of samples to store for each metric.  Only necessary
     * summaries are kept for calculating MAX/MIN/STDEV and average for
     * those beyond the latest samples.
     */
    public void setMaxSamples(int maxSamples) {
        m_maxSamples = maxSamples;
    }

    /**
     * -----------------------------------------------------------------
     */
    public void addMetricValue(String metric,double value) {
        MetricRecord r = (MetricRecord)m_metrics.get(metric);
        if (r == null) {
            r = new MetricRecord();
            r.m_name = metric;
            m_metrics.put(metric,r);
        }

        r.addSample(value);
    }

    /**
     * -----------------------------------------------------------------
     * @return The list of metric names.
     */
    public Enumeration getMetrics() {
        return m_metrics.keys();
    }

    /**
     * -----------------------------------------------------------------
     */
    public Vector getSamples(String metric,int samples) {
        MetricRecord r = (MetricRecord)m_metrics.get(metric);

        if (r == null) {
            throw new IllegalArgumentException("No metric: "+metric+" found");
        }

        return r.getSamples(samples);
    }

    /**
     * -----------------------------------------------------------------
     * @param  metric     The metric name.
     * @param  numSamples calculate over these # of latest samples
     * @param  type       The type of metric.
     * @return the value specified by type calculated over the last numSamples.
     * @exception IllegalArgumentException invalid metric type or numSamples
     *             beyond the amount of data available.
     */
    public double getMetricStat(String metric,
                                int numSamples,
                                byte type) throws IllegalArgumentException {
        MetricRecord r = (MetricRecord)m_metrics.get(metric);

        if (r == null) {
            throw new IllegalArgumentException("No metric: "+metric+" found");
        }

        switch(type) {
            case MIN_STAT:
                return r.getMin(numSamples);
            case MAX_STAT:
                return r.getMax(numSamples);
            case AVG_STAT:
                return r.getAvg(numSamples);
            case JITTER_STAT:
                return r.getJitter(numSamples);
            case TIME_AVG_STAT:
                return r.getTimeAvg(numSamples);
            default:
                throw new IllegalArgumentException("Invalid type: "+type);
        }
    }

     /**
     * -----------------------------------------------------------------
     * byte MIN_STAT, MAX_STAT, AVG_STAT,
     * @exception IllegalArgumentException, invalid type or metric name.
     */
    public double getMetricStat(String metric,
                                long startPeriod,
                                long endPeriod,
                                byte type) throws IllegalArgumentException {
        MetricRecord r = (MetricRecord)m_metrics.get(metric);

        if (r == null) {
            if (type == TIME_AVG_STAT) {
                return 0;
            }
            throw new IllegalArgumentException("No metric: "+metric+" found");
        }

        switch(type) {
            case MIN_STAT:
                return r.getMin(startPeriod,endPeriod);
            case MAX_STAT:
                return r.getMax(startPeriod,endPeriod);
            case AVG_STAT:
                return r.getAvg(startPeriod,endPeriod);
            case JITTER_STAT:
                return r.getJitter(startPeriod,endPeriod);
            case TIME_AVG_STAT:
                return r.getTimeAvg(startPeriod,endPeriod);
            default:
                throw new IllegalArgumentException("Invalid type: "+type);
        }
    }

    /**
     * ----------------------------------------------------------------------
     * @exception IllegalArgumentException the metric is not found.
     */
    public double getLastSample(String metric) {
        MetricRecord r = (MetricRecord)m_metrics.get(metric);

        if (r == null) {
            throw new IllegalArgumentException("No metric: "+metric+" found");
        }

        return r.getSample(0);
    }

    /**
     * =====================================================================
     */
    class Sample {
        long    m_sampleTime = System.currentTimeMillis();
        double  m_sample;

        Sample(double d) {
            m_sample = d;
        }
    }

    /**
     * =====================================================================
     */
    class MetricRecord {
        String  m_name    = "";
        Vector  m_samples = new Vector();  // The sample history list.
        double  m_total = 0;  // The total value of samples from before
                              // not including those stored in the m_samples
        long    m_numSamples = 0; // num samples before the 1st item in the history list.
        long    m_firstSampleTime = 0; // First time the sample was ever taken.
        long    m_firstValidSampleTime = 0; // The time the 1st sample in the history lst was taken.
        double  m_max   = 0;           // Min now.
        double  m_min   = Double.MAX_VALUE; // Min up to now.

        /**
         * -----------------------------------------------------------------
         * Check to see if the asked for number of samples exists.
         * @exception IllegalArgumentException No data available for number of
         *            samples specified.
         */
        void checkDataAvailable(int numSamples) throws IllegalArgumentException {
            if (numSamples > m_samples.size()) {
                throw new IllegalArgumentException("Only have "+m_samples.size()+
                    " samples but asking for "+numSamples);
            }
        }

        /**
         * -----------------------------------------------------------------
         * Check to see if the asked for startPeriod or endPeriod is not within
         * the range for which we can calculate (due to limited size of history
         * list)
         * @exception IllegalArgumentException No data available for the time
         *          range specified.
         */
        void checkDataAvailable(long startPeriod,long endPeriod) {
//System.out.println("["+m_name+"]: startPeriod="+startPeriod+" 1st valid: "+m_firstValidSampleTime);
            if (startPeriod <= m_firstValidSampleTime && startPeriod != 0) {
                throw new IllegalArgumentException("Data Not Available.  Only period from "+
                                                    m_firstValidSampleTime+" or 0 is available.  "+
                                                    startPeriod+" to "+endPeriod+" requested.");
            }
        }

        /**
         * -----------------------------------------------------------------
         * Retrieves the ith sample where 0 is the latest.
         *
         * @param  num The num-th sample to return;
         * @return The sample value.
         * @exception IllegalArgumentException data not available.
         */
        double getSample(int num) throws IllegalArgumentException {
            checkDataAvailable(1);

            return ((Sample)m_samples.elementAt(0)).m_sample;
        }

        /**
         * -----------------------------------------------------------------
         * @exception IllegalArgumentException No data available for the time
         *          range specified.
         */
        double getMin(long startPeriod,long endPeriod) throws IllegalArgumentException {
            double min = Double.MAX_VALUE;

            checkDataAvailable(startPeriod,endPeriod);

            if (startPeriod < m_firstValidSampleTime) {
                return m_min;
            }

            Enumeration e = m_samples.elements();
            while(e.hasMoreElements()) {
                Sample s = (Sample)e.nextElement();
                if (s.m_sampleTime >= endPeriod) break;
                if (s.m_sampleTime >= startPeriod && s.m_sample < min) {
                    min = s.m_sample;
                }
            }
            return min;
        }

        /**
         * -----------------------------------------------------------------
         * @exception IllegalArgumentException No data available for the time
         *          range specified.
         */
        double getMax(long startPeriod,long endPeriod) {
            double max = 0;

            checkDataAvailable(startPeriod,endPeriod);

            if (startPeriod < m_firstValidSampleTime) {
                return m_max;
            }

            Enumeration e = m_samples.elements();
            while(e.hasMoreElements()) {
                Sample s = (Sample)e.nextElement();
                if (s.m_sampleTime >= endPeriod) break;
                if (s.m_sampleTime >= startPeriod && s.m_sample > max) {
                    max = s.m_sample;
                }
            }
            return max;
        }

        /**
         * -----------------------------------------------------------------
         * @exception IllegalArgumentException No data available for the time
         *          range specified.
         */
        double getAvg(long startPeriod,long endPeriod) {
            double total = 0;
            long   numSamples = 0;

            checkDataAvailable(startPeriod,endPeriod);

            if (startPeriod < m_firstValidSampleTime) {
                total = this.m_total;
                numSamples = this.m_numSamples;
            }

            Enumeration e = m_samples.elements();
            while(e.hasMoreElements()) {
                Sample s = (Sample)e.nextElement();
                if (s.m_sampleTime >= endPeriod) break;
                if (s.m_sampleTime >= startPeriod) {
                    total += s.m_sample;
                    numSamples++;
                }
            }
            return total / numSamples;
        }

        /**
         * -----------------------------------------------------------------
         * @exception IllegalArgumentException No data available for the time
         *          range specified.
         */
        double getTimeAvg(long startPeriod,long endPeriod) {
            double total = 0;
            long firstTime = 0;
            long lastTime = 0;

            try {
                checkDataAvailable(startPeriod,endPeriod);
            } catch (IllegalArgumentException ex) {
                return 0;
            }

            if (startPeriod < m_firstValidSampleTime) {
                total = this.m_total;
                firstTime = m_firstSampleTime;
            }

            Enumeration e = m_samples.elements();
            Sample s = null;
            while(e.hasMoreElements()) {
                s = (Sample)e.nextElement();
                if (s.m_sampleTime >= endPeriod) {
                    break;
                }
                if (s.m_sampleTime >= startPeriod) {
                    if (firstTime == 0) {
                        firstTime = s.m_sampleTime;
                    }
                    total += s.m_sample;
                    lastTime = s.m_sampleTime;
                }
            }
            return total * 1000 / (lastTime - firstTime);
        }

        /**
         * -----------------------------------------------------------------
         * @exception IllegalArgumentException No data available for the time
         *          range specified.
         */
        double getJitter(long startPeriod,long endPeriod) throws IllegalArgumentException {
            double total = 0;
            long   numSamples = 0;

            if (startPeriod < m_firstValidSampleTime) {
                throw new IllegalArgumentException("No data available");
            }

            Vector data = new Vector();
            Enumeration e = m_samples.elements();
            double lastTime = 0;
            while(e.hasMoreElements()) {
                Sample s = (Sample)e.nextElement();
                if (s.m_sampleTime >= endPeriod) break;
                if (s.m_sampleTime >= startPeriod) {
                    if (lastTime == 0) {
                        lastTime = s.m_sample;
                    } else {
                        data.addElement(new Double(s.m_sample - lastTime));
                        lastTime = s.m_sample;
                    }
                }
            }

            return calculateJitter(data);
        }

        /**
         * -----------------------------------------------------------------
         * @exception IllegalArgumentException No data available for the time
         *          range specified.
         */
        synchronized double getMin(int numSamples) throws IllegalArgumentException {
            double min = Double.MAX_VALUE;

            checkDataAvailable(numSamples);

            for(int i=0;i < numSamples;i++) {
                Sample s = (Sample)m_samples.elementAt(m_samples.size()-i-1);
                if (s.m_sample < min) {
                    min = s.m_sample;
                }
            }

            return min;
        }

        /**
         * -----------------------------------------------------------------
         * @exception IllegalArgumentException No data available for the time
         *          range specified.
         */
        synchronized double getMax(int numSamples) {
            double max = 0;

            checkDataAvailable(numSamples);

            for(int i=0;i < numSamples;i++) {
                Sample s = (Sample)m_samples.elementAt(m_samples.size()-i-1);
                if (s.m_sample > max) {
                    max = s.m_sample;
                }
            }

            return max;
        }

        /**
         * -----------------------------------------------------------------
         * @exception IllegalArgumentException No data available for the time
         *          range specified.
         */
        synchronized double getAvg(int numSamples) throws IllegalArgumentException {
            double total = 0;

            checkDataAvailable(numSamples);

            for(int i=0;i < numSamples;i++) {
                Sample s = (Sample)m_samples.elementAt(m_samples.size()-i-1);
                total += s.m_sample;
            }

            return total / numSamples;
        }

        /**
         * -----------------------------------------------------------------
         * @exception IllegalArgumentException No data available for the time
         *          range specified.
         */
        synchronized double getTimeAvg(int numSamples) {
            double total = 0;
            long firstTime = 0;
            long lastTime  = 0;

            try {
                checkDataAvailable(numSamples);
            } catch (IllegalArgumentException ex) {
                return 0;
            }

            for(int i=0;i < numSamples;i++) {
                Sample s = (Sample)m_samples.elementAt(m_samples.size()-i-1);
                firstTime = s.m_sampleTime;
                if (lastTime == 0) {
                    lastTime = s.m_sampleTime;
                }
                total += s.m_sample;
            }

            return total * 1000 / (lastTime - firstTime);
        }

        /**
         * -----------------------------------------------------------------
         * @exception IllegalArgumentException No data available for the time
         *          range specified.
         */
        synchronized double getJitter(int numSamples) throws IllegalArgumentException {
            checkDataAvailable(numSamples);

            double lastTime = 0;
            Vector data = new Vector();

            for(int i=0;i < numSamples;i++) {
                Sample s = (Sample)m_samples.elementAt(m_samples.size()-i-1);
                if (lastTime == 0) {
                   lastTime = s.m_sample;
                } else {
                    data.addElement(new Double(lastTime - s.m_sample));
                    lastTime = s.m_sample;
                }
            }

            return calculateJitter(data);
        }

        /**
         * -----------------------------------------------------------------
         */
        synchronized Vector getSamples(int numSamples) {
            Vector result = new Vector();

            for(int i=0;i < numSamples;i++) {
                Sample s = (Sample)m_samples.elementAt(m_samples.size()-i-1);
                result.addElement(new Double(s.m_sample));
            }
            return result;
        }

        /**
         * -----------------------------------------------------------------
         */
        synchronized void addSample(double f) {
            if (m_firstSampleTime == 0) {
                m_firstValidSampleTime = System.currentTimeMillis();
                m_firstSampleTime = System.currentTimeMillis();
            }

            m_samples.addElement(new Sample(f));

            if (f < m_min) {
                m_min = f;
            }

            if (f > m_max) {
                m_max = f;
            }

            while(m_samples.size() > m_maxSamples) {
                double d = ((Sample)m_samples.elementAt(0)).m_sample;
                m_total += d;
                m_numSamples++;
                // Should be the remove item's time.  Because any time after this
                // is valid.
                m_firstValidSampleTime = ((Sample)m_samples.elementAt(0)).m_sampleTime;
                m_samples.removeElementAt(0);
//System.out.println("["+m_name+"]: 1st sample="+((Sample)m_samples.elementAt(0)).m_sample+
//                   " 1st sample time="+((Sample)m_samples.elementAt(0)).m_sampleTime);
            }
        }

        /**
         * -------------------------------------------------------------
         * @param data A vector of Doubles that contain inter-arrival time.
         */
        private double calculateJitter(Vector data) {
            Collections.sort(data);

            int percent25Index = (int)Math.ceil(data.size() * 0.25 - 0.99);
            int percent75Index = (int)Math.ceil(data.size() * 0.75 - 1);
/*System.out.print("[jitter,"+percent25Index+"=>"+percent75Index+"]: ");
for(int i=0;i < data.size();i++) {
    System.out.print(data.elementAt(i)+" ");
}
System.out.println();*/
            if (percent75Index == percent25Index) {
                percent75Index = percent25Index;
            }
            if (percent75Index >= data.size()) {
                percent75Index = data.size()-1;
            }
            double val25 = ((Double)data.elementAt(percent25Index)).doubleValue();
            double val75 = ((Double)data.elementAt(percent75Index)).doubleValue();
            return (val75 - val25);
        }
    }

    /**
     * =====================================================================
     */
    public static void main(String[] args) {
        StatCollection c = new StatCollection();

        c.setMaxSamples(5);

        int total = 0;
        long lastPeriod = 0;
        long invalidPeriod = 0;
        long lastPeriodTotal = 0;
        long lastPeriodTotal2 = 0;
        for(int i=0;i < 20;i++) {
            total += i;
            if (i == 15) {
                try { Thread.currentThread().sleep(100); } catch (Exception ex) {}
                lastPeriod = System.currentTimeMillis();
                try { Thread.currentThread().sleep(100); } catch (Exception ex) {}
            }
            if (i == 14) {
                try { Thread.currentThread().sleep(100); } catch (Exception ex) {}
                invalidPeriod = System.currentTimeMillis();
            }
            if (i >= 15) {
                lastPeriodTotal += i;
                lastPeriodTotal2 += (19-i);
            }
            c.addMetricValue("normal",i);
            c.addMetricValue("inverse",(19-i));
        }

        try { Thread.currentThread().sleep(500); } catch (Exception ex) {}
        long time = System.currentTimeMillis();
        System.out.println("===============LifeTime Values==================");
        System.out.println("Correct Avg: "+(double)total/20);
        System.out.print("norml MIN stat: ");
        if (c.getMetricStat("normal",0,time,MIN_STAT) != 0) {
            System.out.println("FAILED!");
        } else {
            System.out.println("Passed");
        }
        System.out.print("norml MAX stat: ");
        if (c.getMetricStat("normal",0,time,MAX_STAT) != 19) {
            System.out.println("FAILED!");
        } else {
            System.out.println("Passed");
        }
        System.out.print("norml AVG stat: ");
        if (c.getMetricStat("normal",0,time,AVG_STAT) != (double)total/20) {
            System.out.println("FAILED!");
        } else {
            System.out.println("Passed");
        }

        System.out.print("norml MIN stat: ");
        if (c.getMetricStat("inverse",0,time,MIN_STAT) != 0) {
            System.out.println("FAILED!");
        } else {
            System.out.println("Passed");
        }
        System.out.print("norml MAX stat: ");
        if (c.getMetricStat("inverse",0,time,MAX_STAT) != 19) {
            System.out.println("FAILED!");
        } else {
            System.out.println("Passed");
        }
        System.out.print("norml AVG stat: ");
        if (c.getMetricStat("inverse",0,time,AVG_STAT) != (double)total/20) {
            System.out.println("FAILED!");
        } else {
            System.out.println("Passed");
        }
        /*Enumeration e = c.getMetrics();
        while(e.hasMoreElements()) {
            String name = (String)e.nextElement();
            System.out.println(name+": min="+c.getMetricStat(name,0,time,MIN_STAT)+
                               " max="+c.getMetricStat(name,0,time,MAX_STAT)+
                               " avg="+c.getMetricStat(name,0,time,AVG_STAT));
        }*/

        time = System.currentTimeMillis();
        System.out.println("===============Illegal Data Range Test==================");
        System.out.println("Invalid Start Time: "+invalidPeriod);
        Enumeration e = c.getMetrics();
        while(e.hasMoreElements()) {
            String name = (String)e.nextElement();
            try {
                c.getMetricStat(name,invalidPeriod,time,MIN_STAT);
                System.out.println(name+" MIN stat: FAILED!");
            } catch (Exception ex) {
                System.out.println(name+" MIN stat: Passed");
            }

            try {
                c.getMetricStat(name,invalidPeriod,time,MAX_STAT);
                System.out.println(name+" MAX stat: FAILED!");
            } catch (Exception ex) {
                System.out.println(name+" MAX stat: Passed");
            }

            try {
                c.getMetricStat(name,invalidPeriod,time,AVG_STAT);
                System.out.println(name+" AVG stat: FAILED!");
            } catch (Exception ex) {
                System.out.println(name+" AVG stat: Passed");
            }
        }

        System.out.println("===============Last[5] Time Period Values==================");
        System.out.println("Correct normal Avg: "+(double)lastPeriodTotal/5);
        System.out.println("Correct inverse Avg: "+(double)lastPeriodTotal2/5);
        System.out.print("normal MIN stat: ");
        if (c.getMetricStat("normal",lastPeriod,time,MIN_STAT) != 15) {
            System.out.println("FAILED!");
        } else {
            System.out.println("PASSED");
        }

        System.out.print("normal MAX stat: ");
        if (c.getMetricStat("normal",lastPeriod,time,MAX_STAT) != 19) {
            System.out.println("FAILED!");
        } else {
            System.out.println("PASSED");
        }

        System.out.print("normal AVG stat: ");
        if (c.getMetricStat("normal",lastPeriod,time,AVG_STAT) != 17) {
            System.out.println("FAILED!");
        } else {
            System.out.println("PASSED");
        }

        System.out.print("inverse MIN stat: ");
        if (c.getMetricStat("inverse",lastPeriod,time,MIN_STAT) != 0) {
            System.out.println("FAILED!");
        } else {
            System.out.println("PASSED");
        }

        System.out.print("inverse MAX stat: ");
        if (c.getMetricStat("inverse",lastPeriod,time,MAX_STAT) != 4) {
            System.out.println("FAILED!");
        } else {
            System.out.println("PASSED");
        }

        System.out.print("inverse AVG stat: ");
        if (c.getMetricStat("inverse",lastPeriod,time,AVG_STAT) != 2) {
            System.out.println("FAILED!");
        } else {
            System.out.println("PASSED");
        }

        System.out.println("===============Last[3] Sample Values==================");
        System.out.println("Correct normal Avg: 18");
        System.out.println("Correct inverse Avg: 1");
        System.out.print("normal MIN stat: ");
        if (c.getMetricStat("normal",3,MIN_STAT) != 17) {
            System.out.println("FAILED!");
        } else {
            System.out.println("PASSED");
        }

        System.out.print("normal MAX stat: ");
        if (c.getMetricStat("normal",3,MAX_STAT) != 19) {
            System.out.println("FAILED!");
        } else {
            System.out.println("PASSED");
        }

        System.out.print("normal AVG stat: ");
        if (c.getMetricStat("normal",3,AVG_STAT) != 18) {
            System.out.println("FAILED!");
        } else {
            System.out.println("PASSED");
        }

        System.out.print("inverse MIN stat: ");
        if (c.getMetricStat("inverse",3,MIN_STAT) != 0) {
            System.out.println("FAILED!");
        } else {
            System.out.println("PASSED");
        }

        System.out.print("inverse MAX stat: ");
        if (c.getMetricStat("inverse",3,MAX_STAT) != 2) {
            System.out.println("FAILED!");
        } else {
            System.out.println("PASSED");
        }

        System.out.print("inverse AVG stat: ");
        if (c.getMetricStat("inverse",3,AVG_STAT) != 1) {
            System.out.println("FAILED!");
        } else {
            System.out.println("PASSED");
        }

        c = new StatCollection();
        c.setMaxSamples(10);
        long[] arrivalTimes = { 0, 10, 30, 35, 45, 65, 70, 80, 100, 105, 115, 135, 140 };
        //Jitter for last 10 samples: 5 5 5 10 10 10 20 20 20
        for(int i=0;i < arrivalTimes.length;i++) {
            c.addMetricValue("jitter",arrivalTimes[i]);
        }

        System.out.println("=================JITTER TESTS=================");
        System.out.println("Correct last 10 sample JITTER Stat: 15");
        System.out.print("Last 10 samples JITTER stat: ");
        if (c.getMetricStat("jitter",10,JITTER_STAT) != 15) {
            System.out.println("FAILED!: "+c.getMetricStat("jitter",10,JITTER_STAT));
        } else {
            System.out.println("PASSED");
        }

        System.out.print("Invalid JITTER Sample: ");
        try {
            c.getMetricStat("jitter",11,JITTER_STAT);
            System.out.println("FAILED");
        } catch (Exception ex) {
            System.out.println("PASSED");
        }

        System.out.println("=================Time Avg Tests=================");
        double[] values = { 1000, 5000, 10000, 7000, 8000, 9000, 3000 };
        long starttime = System.currentTimeMillis();
        total = 0;
        for(int i=0;i < values.length;i++) {
            c.addMetricValue("tavg",values[i]);
            try { Thread.currentThread().sleep(100); } catch (Exception ex) {}
            total += values[i];
        }
        long endtime = System.currentTimeMillis() - 100;
        double correctVal = total * 1000 / (endtime - starttime);
        System.out.println("Correct time avg value: "+correctVal+"/sec");
        System.out.print("Last "+values.length+" Samples Time Avg: ");
        if (Math.abs(c.getMetricStat("tavg",values.length,TIME_AVG_STAT) - correctVal) >
            correctVal * 0.1) {
            System.out.println("FAILED!: "+c.getMetricStat("tavg",values.length,TIME_AVG_STAT));
        } else {
            System.out.println("PASSED");
        }
    }
}
